/* Copyright (C) 2010 Fredrique Samuels, fredriquesamuels@gmail.com 

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <math.h>
#include <assert.h>

#include "animate.h"

namespace tri
{

float animateLinear(const float& start_value,
        const float& end_value,
        TProgressTimer& timer,
        const float& current_value){
	
    if( current_value == end_value ){
        return current_value;
    }
    if ( start_value < end_value ){
        if( current_value >= end_value ){
            return current_value;
        }
    }
    else{
        if( current_value < end_value ){
            return current_value;
        }
    }

    return start_value + ( timer.getProgress() * ( end_value - start_value ) );
}

float animateExp(const float& start_value,
        const float& end_value,
        TProgressTimer& timer,
        const float& current_value){
	
    float prog = timer.getProgress();
    
    if( prog == 1.0 || current_value == end_value ){
        return end_value;
    }

    // when animation progress = 1, the current value is 99.9% of the end_value
    return start_value + ( 1-exp(- ( 7*prog ) )) * ( end_value - start_value );
}

//==============================================

TAniVar::TAniVar():value(0.0),end_val(0.0),start_val(0.0),animator(LINEAR_ANIMATE){
    timer.setMode(TIMER_TYPE);
}

TAniVar::~TAniVar()
{
}

float TAniVar::getValue(){
    if ( timer.isRunning() ){
        value = animator(start_val,end_val,timer,value);
        if ( value == end_val ){
            setValue(value);
        }
    }
    return value;
}

void TAniVar::setValue(const float& v){
    value = start_val = end_val = v;
    timer.stop();
}

void TAniVar::setTargetValue(const float&  v){
//	if (end_val == v){
//		return;
//	}
    end_val = v;
    start_val = value;
    timer.reset();
}

float TAniVar::getTargetValue(){
	return end_val;
}

void TAniVar::setTargetValue(const float&  v, const float&  t){
//	if (end_val == v){
//		return;
//	}
    timer.setDurationTime(t);
    setTargetValue(v);
}

void TAniVar::setAnimator(float (*ani)(const float&,const float&,tri::TProgressTimer&,const float&)){
    assert( ani == LINEAR_ANIMATE || ani == EXP_ANIMATE );
    timer.stop();
    this->animator = animator;
}

void TAniVar::add(float& v){
    setValue(getValue()+v);
}
void TAniVar::mult(float& v){
    setValue(getValue()*v);
}


bool TAniVar::isAnimating(){
	return timer.isRunning();
}

//==============================================

TAniVector::TAniVector(){}
TAniVector::TAniVector(const float& x,const float& y,const float& z){
    setValue(x,y,z);
}
TAniVector::TAniVector(const float* v){
    setValue(v);
}

TAniVector::~TAniVector(){}

bool TAniVector::isAnimating(){
	return x.isAnimating() or y.isAnimating() or z.isAnimating();
}

void TAniVector::getValue(float * v){
    v[0] = x.getValue();
    v[1] = y.getValue();
    v[2] = z.getValue();
}

tri::TVector TAniVector::getValue(){
	return tri::TVector(x.getValue(),y.getValue(),z.getValue());
}

void TAniVector::getValue(TVector *v){
    v->x = x.getValue();
    v->y = y.getValue();
    v->z = z.getValue();
}

float TAniVector::getX(){
    return x.getValue();
}

float TAniVector::getY(){
    return y.getValue();
}

float TAniVector::getZ(){
    return z.getValue();
}

tri::TVector TAniVector::getTargetValue(){
	return tri::TVector(x.getTargetValue(), y.getTargetValue(), z.getTargetValue());
}

void TAniVector::setValue(const float& x,const float& y,const float& z){
    this->x.setValue(x);
    this->y.setValue(y);
    this->z.setValue(z);
}

void TAniVector::setValue(tri::TVector v){
	this->x.setValue(v.x);
    this->y.setValue(v.y);
    this->z.setValue(v.z);
}

void TAniVector::setValue(const float * v){
    setX(v[0]);
    setY(v[1]);
    setZ(v[2]);
}

void TAniVector::setValue(const TVector * v){
    setX(v->x);
    setY(v->y);
    setZ(v->z);
}

void TAniVector::setX(const float& v){
    this->x.setValue(v);
}

void TAniVector::setY(const float& v){
    this->y.setValue(v);
}

void TAniVector::setZ(const float& v){
    this->z.setValue(v);
}

void TAniVector::setTargetValue(const float *v,const float& t){
    setTargetValueX(v[0],t);
    setTargetValueY(v[1],t);
    setTargetValueZ(v[2],t);
}

void TAniVector::setTargetValue(const TVector *v,const float& t){
    setTargetValueX(v->x,t);
    setTargetValueY(v->y,t);
    setTargetValueZ(v->z,t);
}

void TAniVector::setTargetValue(const float& x,const float& y,const  float& z,const float& t){
    setTargetValueX(x,t);
    setTargetValueY(y,t);
    setTargetValueZ(z,t);
}

void TAniVector::setTargetValueX(const float &value,const float& t){
    x.setTargetValue(value,t);
}
void TAniVector::setTargetValueY(const float &value,const float& t){
    y.setTargetValue(value,t);
}
void TAniVector::setTargetValueZ(const float &value,const float& t){
    z.setTargetValue(value,t);
}
void TAniVector::setAnimator(float (*animator)(const float&,
        const float&,
        tri::TProgressTimer&,
        const float&)){
    x.setAnimator(animator);
    y.setAnimator(animator);
    z.setAnimator(animator);
}

void TAniVector::add(float x,float y,float z){
    this->x.add(x);
    this->y.add(y);
    this->z.add(z);
}

tri::TAniVector& TAniVector::operator=(const tri::TVector &v){
	this->setValue(v.x, v.y, v.z);
	return *(this);
}

}
